(function() {

    /**
     * @class Ext.ux.grid.VarHeaders
     *
     * Plugin for GridPanel that enables configuration of three different headers which are shown depending on the available column width:
     * longText, text, shortText.
     *
     * Plugin alias is 'varheaders' (use "plugins: 'varheaders'" in GridPanel config).
     *
     * @author <a href="mailto:stephen.friedrich@fortis-it.de">Stephen Friedrich</a>
     * @author <a href="mailto:fabian.urban@fortis-it.de">Fabian Urban</a>
     *
     * @copyright (c) 2011 Fortis IT Services GmbH
     * @license Ext.ux.grid.VarHeaders is released under the
     * <a target="_blank" href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.
     *
     */
    Ext.define('Ext.ux.grid.VarHeaders', {
                   alias: 'plugin.varheaders',

                   /**
                    * Called by plug-in system to initialize the plugin for a specific grid panel
                    */
                   init: function(grid) {
                       grid.on('afterrender',
                               function(){
                                    Ext.Array.forEach(grid.query('headercontainer'), this.initColumn, this);
                               },
                               this);
                   },

                   initColumn: function(column) {
                       this.addListeners(column);
                       this.updateColumnTextIfNeeded(column);
                   },

                   /**
                    * Attach listeners to a column for resize (update text), sort change (required width changes due to sort icon, so text
                    * might need to be updated, too) and add (new sub columns that also need to be listened to).
                    */
                   addListeners: function(headerCt) {
                       if (!headerCt.varHeadersListeners) {
                           headerCt.on('add', this.handleColumnAdded, this);
                           headerCt.on('columnresize', this.handleColumnHeaderResize, this);
                           headerCt.on('sortchange', this.handleColumnSortChange, this);

                           // Backup standard text value (headerCt.text changes when another text is set after resizing the column)
                           headerCt.normalText = headerCt.text;

                           // Set a flag, so that listeners aren't added twice (when columns are removed/added)
                           headerCt.varHeadersListeners = true;
                       }

                       return true;
                   },

                   handleColumnAdded: function(headerContainer, component, index) {
                       this.initColumn(component);
                   },

                   handleColumnHeaderResize: function(headerContainer, column, width) {
                       this.updateColumnTextIfNeeded(column);
                   },

                   handleColumnSortChange: function(headerContainer, column, direction) {
                       this.updateColumnTextIfNeeded(column);
                   },

                   /**
                    * Update the column's text to one of shortText, normalText (contains saved original text) and longText depending
                    * on current column width.
                    */
                   updateColumnTextIfNeeded: function(column) {
                       var textElement = column.textEl; // textEL is set via renderSelector in Ext.grid.column.Column)
                       if (!textElement) {
                           return;
                       }

                       var availableWidth = column.getWidth();
                       var columnEl = column.el;
                       var el = textElement;
                       while(true) {
                           availableWidth -= (el.getPadding('lr') + el.getMargin('lr') + el.getBorderWidth('lr'));
                           if(el == columnEl) {
                               break;
                           }
                           el = el.parent();
                       }

                       var newText = this.findMatchingText(availableWidth, textElement,
                                                           [column.longText, column.normalText, column.shortText]);
                       if (newText != column.text) {
                           column.setText(newText);
                       }
                   },

                   /**
                    * Returns the first text from "texts" array that is at most "availableWidth" wide when rendered with same CSS as
                    * "element". If no text matches the last non-null entry of "texts" is returned.
                    * null entries in the "texts" array are skipped.
                    */
                   findMatchingText: function(availableWidth, element, texts) {
                       var newText = null;
                       for(var i = 0, length = texts.length; i < length; ++i) {
                           var text = texts[i];
                           if(!text) {
                               continue;
                           }
                           newText = text;
                           var width = Ext.util.TextMetrics.measure(element, text).width;
                           if(width <= availableWidth) {
                               break;
                           }
                       }
                       return newText;
                   }
               });
})();
